我將 TodoTask 跟 DoneTask 個別獨立成 Component
在由 Task.vue 引入 Component,以下便是 template 內容
<template>
<div class="task">
<TodoTask />
<DoneTask />
<v-fab-transition>
<v-btn>
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-fab-transition>
<v-dialog></v-dialog>
</div>
</template>
TodoTask 跟 DoneTask 的作法基本上一樣,所以直接說明其中一個的做法
直接使用 vuetify 提供的 v-list 當最主元件
每一筆 Task 都由 v-list-item 呈現
我的設計是,當 List 超出版面時就能 Scroll ,滾軸滑到底時就會觸發事件並載入後續的資料
這邊的事件觸發使用 vuetify 提供的 Intersection Observer API ,當滾軸滑到最後一筆時,v-intersect 就會被觸發,接著在判斷是否要仔入資料或是要載入哪些資料
最後再將取得的資料存入 vue apollo 的緩存中,畫面也會渲染成最新的資料
以 DoneTask 來說
當滾軸滑到底需要載入新資料時,用 $emit 傳遞事件給父層 Task.vue ,由父層實作
取得新資料後再經由 Prop 將資料傳給子層 DoneTask.vue
這樣就能更新 DoneTask 的資料
<v-list two-line>
<v-subheader>DONE</v-subheader>
<v-divider></v-divider>
<v-list-item-group v-if="doneTasks">
<template v-for="(task, index) in doneTasks.tasks" >
<v-list-item v-if="doneTasks.tasks.length - 1 > index" :key="task.id" >
<v-list-item-content>
<v-list-item-title>{{task.title}}</v-list-item-title>
<v-list-item-subtitle>{{task.content}}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-icon color="green lighten-1">mdi-check</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item v-else :key="task.id" v-intersect="loadDataDoneTask" >
<v-list-item-content>
<v-list-item-title>{{task.title}}</v-list-item-title>
<v-list-item-subtitle>{{task.content}}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-icon color="green lighten-1">mdi-check</v-icon>
</v-list-item-action>
</v-list-item>
</template>
</v-list-item-group>
</v-list>
<script>
export default {
props: ['doneTasks'],
methods: {
loadDataDoneTask(entries, observer, isIntersecting) {
if (!isIntersecting) return;
if (this.doneTasks.taskCount == 0 || this.doneTasks.tasks.length === this.doneTasks.taskCount) return;
this.$emit('loadDataDoneTask');
}
}
}
</script>
在 Task.vue 的實作
vue apollo 的 Smart Query 提供 fetchMore 的方法,主要目的就是做分頁
在 updateQuery 中能取得未更新的資料以及呼叫分頁資料回來的結果
做一些邏輯運算後,將結果回傳給緩存,這樣就成功更新緩存裡的資料
loadDataDoneTask() {
const currentPageCount = this.doneTaskArgs.pageSize * this.doneTaskArgs.page;
this.doneTaskArgs.start = currentPageCount + this.doneTaskArgs.pageSize;
this.$apollo.queries.doneTasks.fetchMore({
// New variables
variables: this.doneTaskArgs,
// Transform the previous result with new data
updateQuery: (previousResult, { fetchMoreResult, variables }) => {
previousResult.doneTasks.tasks.push(...fetchMoreResult.doneTasks.tasks);
previousResult.doneTasks.taskCount = fetchMoreResult.doneTasks.taskCount;
return {
doneTasks: previousResult.doneTasks
}
},
})
this.doneTaskArgs.page ++;
}
接下來是新增的部分
流程是發送 API 後要更新 TodoTask ,由於不能確定新增的 Task 是目前資料的下一筆,所以沒辦法直接將 Task Push 到 todoTasks 中
透過 Smart Query 的方法 refetch 能夠手動呼叫 API ,這樣便能取得最新前 10 筆資料
雖然這樣做會讓資料一直重撈前 10 筆,但我認為這樣能簡化複雜的流程,所以選擇了這個折衷的辦法
createTask() {
this.$apollo.mutate({
mutation: CREATE_TASK,
variables: {
taskData: this.newTask
},
update: () => {
this.addTaskDialog = false;
this.$apollo.queries.todoTasks.refetch()
Object.assign(this.todoTaskArgs, this.initPageArgs);
this.newTask = {
title: null,
content: null
}
}
})
}
作法與新增 Task 一樣,只是多了更新 DoneTask
async updateTask(data) {
await this.$apollo.mutate({
mutation: UPDATE_TASK,
variables: data
})
this.$apollo.queries.todoTasks.refetch()
this.$apollo.queries.doneTasks.refetch()
Object.assign(this.doneTaskArgs, this.initPageArgs);
Object.assign(this.todoTaskArgs, this.initPageArgs);
},
以上功能都完成,我們的 Todo List 也就完成了!!!
非常簡單的一個小小專案,但是使用到的功能應該也算蠻齊全了,往後你也能跟別人說你是全端(半殘)工程師了